﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace B121 {
    public partial class PackingOutputForm : Form {
        public PackingOutputForm(List<ItemClass> items, List<ContainerClass> containers) {
            InitializeComponent();
            Items = items;
            Containers = containers;
        }

        #region Properties
        public List<ItemClass> Items { get; set; }
        public List<ContainerClass> Containers { get; set; }

        private List<GroupBox> gBoxes;
        private List<PictureBox> pBoxes;
        private List<Button> ShowNextItemButtons;
        private List<Button> ClearContainerButtons;
        private List<Label> OutputLabels;
        private List<Label> ShowItemLabels;

        private List<int> ItemJustShowed;

        private int ZoomLevel { get; set; }
        #endregion

        private void PackingOutputForm_Load(object sender, EventArgs e) {
            Text = "Packer"; // Set title

            RecursiveBestFit packAlg = new RecursiveBestFit(); // Create instance

            PackingReturnValues retVal = packAlg.DoThePacking(Containers, Items); // Pack!

            if (retVal != 0) { // Check error value
                string errs = "Errors: ";
                if ((retVal & PackingReturnValues.ItemDoesNotFit) == PackingReturnValues.ItemDoesNotFit) {
                    errs += Environment.NewLine + "Item does not fit";
                }
                if ((retVal & PackingReturnValues.ItemsTotalVolumeTooBig) == PackingReturnValues.ItemsTotalVolumeTooBig) {
                    errs += Environment.NewLine + "Items total volume too big";
                }
                if ((retVal & PackingReturnValues.ItemsTotalWeightToBig) == PackingReturnValues.ItemsTotalWeightToBig) {
                    errs += Environment.NewLine + "Items total weight too big";
                }
                if ((retVal & PackingReturnValues.ItemBottomAreaTooBig) == PackingReturnValues.ItemBottomAreaTooBig) {
                    errs += Environment.NewLine + "Items total bottom area too big";
                }
                if ((retVal & PackingReturnValues.ItemTooHeavy) == PackingReturnValues.ItemTooHeavy) {
                    errs += Environment.NewLine + "An item too heavy";
                }
                if ((retVal & PackingReturnValues.ItemXTooBig) == PackingReturnValues.ItemXTooBig) {
                    errs += Environment.NewLine + "An item X is too big";
                }
                if ((retVal & PackingReturnValues.ItemYTooBig) == PackingReturnValues.ItemYTooBig) {
                    errs += Environment.NewLine + "An item Y is too big";
                }
                if ((retVal & PackingReturnValues.ItemZTooBig) == PackingReturnValues.ItemZTooBig) {
                    errs += Environment.NewLine + "An item Z is too big";
                }

                MessageBox.Show("Items could not be packed. " + errs, "Could not pack items", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                Close();
            }

            Containers = packAlg.Containers; // Set containers to packed containers

            ZoomLevel = 2;
            CreateOutput(Containers.Count);
            ShowPackedContainers();
        }

        private void ShowPackedContainers() {
            Bitmap bp;
            Graphics g;

            for (int i = 0; i < Containers.Count; i++) {
                bp = new Bitmap(Containers[i].X * ZoomLevel, Containers[i].Y * ZoomLevel);
                g = Graphics.FromImage(bp);

                foreach (ItemClass item in Containers[i].Items) {
                    g.DrawRectangle(new Pen(Color.Black), ItemToShow(Containers[i].Y, item));
                    pBoxes[i].Image = bp;
                    pBoxes[i].Refresh();
                }
            }
        }

        /// <summary>
        /// Updates which item to show
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void ShowNextItemButton_Click(object sender, EventArgs e) {
            Button b = CreateButtonFromSender(sender);
            int containerToShow = GetContainerToShow(b);

            // Do some error checking ?

            // If it is not the last item
            if (ItemJustShowed[containerToShow] < Containers[containerToShow].Items.Count) {
                Bitmap bp = new Bitmap(pBoxes[containerToShow].Image);
                Graphics g = Graphics.FromImage(bp);

                if (ItemJustShowed[containerToShow] > 0) { // Draw previous item
                    g.DrawRectangle(new Pen(Color.Black), ItemToShow(Containers[containerToShow].Y, Containers[containerToShow].Items[ItemJustShowed[containerToShow] - 1]));
                }

                // Draw current item
                g.DrawRectangle(new Pen(Color.Red), ItemToShow(Containers[containerToShow].Y, Containers[containerToShow].Items[ItemJustShowed[containerToShow]]));

                pBoxes[containerToShow].Image = bp; // Show it

                ShowItemLabels[containerToShow].Text = "Item: " +
                    Containers[containerToShow].Items[ItemJustShowed[containerToShow]].Name;

                ItemJustShowed[containerToShow]++;
            } else {
                b.Enabled = false;
            }
        }

        /// <summary>
        /// Calculates an rectangle to show it from the bottom of a picturebox
        /// </summary>
        /// <param name="container">Container in which the item is</param>
        /// <param name="item">Item to show</param>
        /// <returns>A rectangle</returns>
        private Rectangle ItemToShow(int topY, ItemClass item) {
            Rectangle r = item.GetRectangle(ZoomLevel);

            //r.Y = (topY - (item.OffsetY + item.Y)) * ZoomLevel; // Calculate offset Y

            return r;
        }

        /// <summary>
        /// Clears the selected container
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void ClearContainerButton_Click(object sender, EventArgs e) {
            Button b = CreateButtonFromSender(sender);
            int containerToShow = GetContainerToShow(b);

            Bitmap bp = new Bitmap(Containers[containerToShow].X * ZoomLevel, Containers[containerToShow].Y * ZoomLevel);
            pBoxes[containerToShow].Image = bp;
            ItemJustShowed[containerToShow] = 0; // Reset which item to show
            ShowItemLabels[containerToShow].Text = "Item: ";

            ShowNextItemButtons[containerToShow].Enabled = true;
        }

        /// <summary>
        /// Gets index of container to show
        /// </summary>
        /// <param name="b">Button</param>
        /// <returns>Index of container</returns>
        private int GetContainerToShow(Button b) {
            int containerToShow = -1;

            for (int i = 0; i < Containers.Count; i++) {
                if (Containers[i].Name == b.Name) {
                    containerToShow = i;
                }
            }
            return containerToShow;
        }

        /// <summary>
        /// Creates a button from an object
        /// </summary>
        /// <param name="sender">Object to convert to button</param>
        /// <returns>Button</returns>
        private Button CreateButtonFromSender(object sender) {
            if (sender.GetType() != typeof(Button)) { // Make sure that it actually is a button
                throw new ArgumentException("sender is not a button. We made a mistake, sorry");
            }
            return (Button)sender;
        }

        #region Prepare to show output
        private void CreateOutput(int numberOfContainers) {
            PrepareControlsLists(); // Initialize lists

            AddItemJustShowed(numberOfContainers); // Which item in a container was just showed
            CreateShowItemLabels(numberOfContainers); // How many items in the container
            CreateGroupBoxes(numberOfContainers); // Create group boxes
            CreatePictureBoxes(numberOfContainers); // Create picture boxes
            CreateShowNextItemButtons(numberOfContainers); // Create "show next item" buttons
            CreateClearContainerButtons(numberOfContainers); // Create "clear container" buttons
            CreateOutputLabels(numberOfContainers); /// Create output labels

            AddControlsToGroupBoxes(numberOfContainers); // Add the controls to the groupboxes

            DisableEmptyContainers(numberOfContainers); // Disable empty containers

            foreach (GroupBox gb in gBoxes) {
                flowLayoutPanel1.Controls.Add(gb); // Add controls to groupboxes
            }
        }

        /// <summary>
        /// Disables containers with no items
        /// </summary>
        /// <param name="numberOfContainers">Number of containers</param>
        private void DisableEmptyContainers(int numberOfContainers) {
            for (int i = 0; i < numberOfContainers; i++) {
                if (Containers[i].Items.Count <= 0) {
                    gBoxes[i].Enabled = false;
                }
            }
        }

        /// <summary>
        /// Prepares the lists for usage
        /// </summary>
        private void PrepareControlsLists() {
            pBoxes = new List<PictureBox>();
            gBoxes = new List<GroupBox>();
            ShowNextItemButtons = new List<Button>();
            ClearContainerButtons = new List<Button>();
            OutputLabels = new List<Label>();
            ItemJustShowed = new List<int>();
            ShowItemLabels = new List<Label>();
        }

        /// <summary>
        /// Adds the index telling which item was just showed in which container
        /// </summary>
        /// <param name="numberOfContainers">Number of containers</param>
        private void AddItemJustShowed(int numberOfContainers) {
            for (int i = 0; i < numberOfContainers; i++) {
                ItemJustShowed.Add(i);
                ItemJustShowed[i] = 0;
            }
        }

        /// <summary>
        /// Adds the controls to the groupboxes
        /// </summary>
        /// <param name="numberOfContainers">Number of groupboxes we have</param>
        private void AddControlsToGroupBoxes(int numberOfContainers) {
            for (int i = 0; i < numberOfContainers; i++) {
                gBoxes[i].Controls.Add(ShowItemLabels[i]);
                gBoxes[i].Controls.Add(pBoxes[i]); // Picture boxes
                gBoxes[i].Controls.Add(ShowNextItemButtons[i]); // "Next item" buttons
                gBoxes[i].Controls.Add(ClearContainerButtons[i]); // "Clear" buttons
                gBoxes[i].Controls.Add(OutputLabels[i]); // Output labels
            }
        }

        /// <summary>
        /// Create "show next item" buttons
        /// </summary>
        /// <param name="numberOfContainers">Number of buttons to create</param>
        private void CreateShowNextItemButtons(int numberOfContainers) {
            for (int i = 0; i < numberOfContainers; i++) {
                Button b = new Button();
                b.Text = "Next item";
                b.Location = new Point(pBoxes[i].Width + 20, pBoxes[i].Location.Y);
                b.Click += new EventHandler(ShowNextItemButton_Click);
                b.Name = Containers[i].Name;
                ShowNextItemButtons.Add(b);
            }
        }

        /// <summary>
        /// Creates "clear container" buttons"
        /// </summary>
        /// <param name="numberOfContainers">Number of buttons to create</param>
        private void CreateClearContainerButtons(int numberOfContainers) {
            for (int i = 0; i < numberOfContainers; i++) {
                Button b = new Button();
                b.Text = "Clear";
                b.Location = new Point(pBoxes[i].Width + 20, ShowNextItemButtons[i].Bottom + 5);
                b.Click += new EventHandler(ClearContainerButton_Click);
                b.Name = Containers[i].Name;
                ClearContainerButtons.Add(b);
            }
        }

        /// <summary>
        /// Creates output labels
        /// </summary>
        /// <param name="numberOfContainers">Number of labels to create</param>
        private void CreateOutputLabels(int numberOfContainers) {
            for (int i = 0; i < numberOfContainers; i++) {
                Label lbl = new Label();
                lbl.AutoSize = true;

                // Text on label
                string weightUsed = Containers[i].WeightUsage.ToString() + "% used of " + (Containers[i].MaxWeight / 1000) + " kg" + Environment.NewLine +
                    "(" + ((double)Containers[i].CurrentWeight / 1000).ToString() + "/" + ((double)Containers[i].MaxWeight / 1000).ToString() + ") kg";
                string bottomAreaUsed = Containers[i].BottomAreaUsage.ToString() + "% used of containers bottom area";
                string volumeUsed = Containers[i].VolumeUsage.ToString() + "% used of total volume";

                lbl.Text = weightUsed + Environment.NewLine + bottomAreaUsed + Environment.NewLine + volumeUsed;
                lbl.Location = new Point(pBoxes[i].Width + 20, ClearContainerButtons[i].Bottom + 10);
                OutputLabels.Add(lbl);
            }
        }

        private void CreateShowItemLabels(int numberOfContainers) {
            for (int i = 0; i < numberOfContainers; i++) {
                Label lbl = new Label();
                lbl.AutoSize = true;
                lbl.Text = "Item: ";
                lbl.Location = new Point(15, 20);
                ShowItemLabels.Add(lbl);
            }
        }

        /// <summary>
        /// Creates groupboxes
        /// </summary>
        /// <param name="numberOfContainers">Number of groupboxes to create</param>
        private void CreateGroupBoxes(int numberOfContainers) {
            for (int i = 0; i < numberOfContainers; i++) {
                GroupBox gb = new GroupBox();
                gb.AutoSize = true;
                gb.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
                gb.Width = 150;
                gb.Height = 200;
                gb.Text = Containers[i].Name + ": " + Containers[i].Items.Count.ToString() + " items";
                gb.Padding = new Padding(5, 5, 13, 0);
                gBoxes.Add(gb);
            }
        }

        /// <summary>
        /// Creates pictureboxes
        /// </summary>
        /// <param name="numberOfContainers">Number of picture boxes to create</param>
        private void CreatePictureBoxes(int numberOfContainers) {
            for (int i = 0; i < numberOfContainers; i++) {
                PictureBox pb = new PictureBox();
                pb.Height = Containers[i].Y * ZoomLevel;
                pb.Width = Containers[i].X * ZoomLevel;
                pb.BorderStyle = BorderStyle.FixedSingle;
                pb.Location = new Point(15, ShowItemLabels[i].Bottom);
                pb.Image = new Bitmap(Containers[i].X * ZoomLevel, Containers[i].Y * ZoomLevel);
                pBoxes.Add(pb);
            }
        }
        #endregion
    }
}
